/****************************************************************************
**
** Authors: Antonio Marcio A Menezes, Emanuel Grohs.
**
** This file is part of the Qt Board Game Framework.
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the
** packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "controler.h"

using namespace qtbf;

/* This flag controls player remote notification.
Only after complex GUI events, like a drag and drop, this flag is setted to true to prevent constant repeated notifications over network. */
private static boolean notificaPlayers = true;

// private static Controle controle;

// TODO aki mesmo e public static?
public static String playerName;

private ListenersVisao listenersVisao;

/* A generic game model reference. */
private BoardGame boardGame;

private Visao visao;

private IPlayer player;

/* List of game players. */
private List<IPlayer> players;

/* Client game state class for dynamic instantiation. */
private Class<E> gameStateClass;

Controler::Controler(QObject *parent) : QObject(parent)
  //Class<J> BoardGame, Class<E> estado
{
    try {
            // this.controle = this;
            this.gameStateClass = estado;
            createGUI(estado);
            createBoardGame(boardGame);
            // player = new Player();
            players = new ArrayList<IPlayer>();
    } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
    } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
    }
}

void Controler::initializeGame() {
    QList<Player*> values = players.values();
    for (int i = 0; i < values.size(); ++i) {
        connect(this, SIGNAL(sendMessageGameState(qtbf::GameState *)), values.at(i), SLOT(receiveMessageGameState(qtbf::GameState *)));
    }
}

/* Instantiate a brand new game state at game begging. */
private void createBoardGame(Class<J> boardGame) throws InstantiationException, IllegalAccessException {
        this.boardGame = BoardGame.newInstance();
        this.boardGame.setMemento();
}

/* Instantiate game GUI. */
private void createGUI(Class<E> estado) throws InstantiationException, IllegalAccessException {
        visao = new Visao();
        listenersVisao = new ListenersVisao(this);
        listenersVisao.addPlayListener();
        listenersVisao.addKeybordListener();
        listenersVisao.addVezListener();
}

/**
 * Escuta alterações no estado de BoardGame e manda executar as notificações
 * remotas.
 */
public final void propertyChange(PropertyChangeEvent evt) {
        String propName = evt.getPropertyName();
        Object valor = evt.getNewValue();
        if ("nPlayers".equals(propName)) {
                visao.setEstadoBoardGame((EstadoBoardGame) BoardGame.criaEstado(gameStateClass, (Integer) valor));
        } else if ("ancestor".equals(propName)) {
                // Quando ocorre modificação de estado pela visao.
                if (notificaPlayers) {
                        if (evt.getOldValue() != null && evt.getNewValue() != null) {
                                // EstadoBoardGame estado = getEstadoBoardGame();
                                // preparaEstado(estado, true);//ja eh feito no
                                // broadcast
                                broadcast(visao.getEstadoBoardGame());
                        }
                }
        } else if (valor instanceof IPlayer) {
                players.add((IPlayer) valor);
                if (BoardGame.getEstadoBoardGame().getnPlayers() == players.size()) {
                        // BoardGame vai comecar
                        setNotificaPlayers(false);
                        EstadoBoardGame estado = BoardGame.configuraEstado(players);
                        setNotificaPlayers(true);
                        // preparaEstado(estado, false); jah eh feito no broadcast
                        visao.setEstadoBoardGame(estado);
                        broadcast(estado);
                }
        } else if (valor instanceof List) {
                players = (List) valor;
        } else if (valor instanceof EstadoBoardGame) {
                EstadoBoardGame estado = (EstadoBoardGame) valor;
                boolean minhaVez = ((Player) players.get(estado.getPlayerVez())).getplayerName().equals(playerName);
                if (!evt.getSource().equals(playerName)) {
                        listenersVisao.preparaEstado(estado, false);
                        BoardGame.setMemento();
                        visao.setEstadoBoardGame(estado);
                        BoardGame.setEstadoBoardGame(estado);
                        // BoardGame.aposReceberEstadoRemoto();
                        // Arruma visao
                        if (minhaVez) {
                                JOptionPane.showMessageDialog(visao, "� sua vez!");
                                // BoardGame.executa(new AbsCommand(COMMAND.PASS, null, null));
                        }
                }
                visao.getButtonDraw().setEnabled(minhaVez);
                visao.getButtonVez().setEnabled(minhaVez);
        }
}

void setNotificaPlayers(boolean notifica) {
        notificaPlayers = notifica;
}

Visao getVisao() {
        return visao;
}

// public final void update(String sender, Object valor) {
// if (valor instanceof String[]) {
// // BoardGame vai comecar
// setNotificaPlayers(false);
// EstadoBoardGame estado = BoardGame.configuraEstado((String[]) valor);
// setNotificaPlayers(true);
// // preparaEstado(estado, false); jah eh feito no broadcast
// visao.setEstadoBoardGame(estado);
// broadcastGameState();
// } else if (valor instanceof EstadoBoardGame) {
// EstadoBoardGame estado = (EstadoBoardGame) valor;
// boolean minhaVez = estado.getPlayerVez().equals(getplayerName());
// if (!sender.equals(getplayerName())) {
// listenersVisao.preparaEstado(estado, false);
// BoardGame.setMemento(estado);
// visao.setEstadoBoardGame(estado);
// // BoardGame.aposReceberEstadoRemoto();
// // Arruma visao
// if (minhaVez) {
// JOptionPane.showMessageDialog(visao, "� sua vez!");
// BoardGame.executa(new AbsCommand(COMMAND.PASS, null, null));
// }
// }
// visao.getButtonDraw().setEnabled(minhaVez);
// visao.getButtonVez().setEnabled(minhaVez);
// }
// }

/* Notify remote players broadcasting sending a changed game state. */
final bool Controler::broadcastGameState() {
        // TODO setChanged(true) aki?
        // player.setChanged(true);
        /*
         * EstadoBoardGame es = null; try { es = (EstadoBoardGame) estado.clone(); } catch
         * (CloneNotSupportedException e) { // TODO Auto-generated catch block
         * e.printStackTrace(); }
         */
        // TODO problema, ao passar true ele acaba com os listeners da visao
        // local.
        // BoardGame.inicializaMementos();
        // BoardGame.addMemento(estado);
        EstadoBoardGame estado = BoardGame.getEstadoBoardGame();
        listenersVisao.preparaEstado(estado, true);
        // player.notifyObservers(estado);
        broadcast(estado);
        listenersVisao.preparaEstado(estado, false);
}

BoardGame getBoardGame() {
        return boardGame;
}

// public EstadoBoardGame undo() {
// EstadoBoardGame ej = BoardGame.undo();
// if (ej != null) {
// estadoBoardGame = ej;
// }
// return estadoBoardGame;
// }

/* Instatiates local player and add it to player list. */
final void createPlayer(String nome) {
        try {
                player = new Player(nome, this);
                playerName = nome;
                // player.addObserver(nome, player);
                addPlayer(player);
        } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
        }
        // getVisao().getEstadoBoardGame().setNumPlayers(numPlayers);
}

/* Add an remote player to players list. */
private final void addPlayer(IPlayer player) {
        if (!players.contains(player)) {
                players.add(player);
        }
        // setChanged(true);
        broadcast(players);
}

private final void broadcast(Object arg) {
        if (arg instanceof EstadoBoardGame) {
                System.out.println("Vai mandar o estado");
        }
        // synchronized (this) {
        // if (!changed)
        // return;
        // setChanged(false);
        // }
        for (IPlayer p : players) {
                try {
                        // if (!((Player) p).playerName.equals(playerName))
                        {
                                p.update(playerName, arg);
                        }
                } catch (RemoteException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                // player.notifyObserver(arg);
        }
}

public IPlayer getPlayer() {
        return player;
}

public List<IPlayer> getPlayers() {
        return players;
}
//
// public EstadoBoardGame getEstadoBoardGame() {
// return visao.getEstadoBoardGame();
// }
